package com.ruoyi.framework.mybatisplus.interceptor;

import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.ruoyi.framework.mybatisplus.handle.GenerateHandler;
import com.ruoyi.framework.mybatisplus.util.InterceptorUtils;
import com.ruoyi.framework.mybatisplus.util.MysqlTableFieldCacheUtils;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.ItemsList;
import net.sf.jsqlparser.expression.operators.relational.MultiExpressionList;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.insert.Insert;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.SelectExpressionItem;
import net.sf.jsqlparser.statement.select.SetOperationList;
import net.sf.jsqlparser.statement.update.Update;
import net.sf.jsqlparser.statement.update.UpdateSet;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Connection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author yql
 * @describe ControllerSqlInterceptor
 * @date 2023/3/8 17:53
 */
public class GenerateInterceptor implements InnerInterceptor {
    private List<GenerateHandler> generateHandlers;

    public List<GenerateHandler> getGenerateHandlers() {
        return generateHandlers;
    }

    public void setGenerateHandlers(List<GenerateHandler> generateHandlers) {
        this.generateHandlers = generateHandlers;
    }

    private static final Logger log = LoggerFactory.getLogger(GenerateInterceptor.class);

    public GenerateInterceptor(List<GenerateHandler> generateInterceptors) {
        this.generateHandlers = generateInterceptors;
    }

    public GenerateInterceptor(GenerateHandler ... generateInterceptor) {
        this.generateHandlers = Arrays.stream(generateInterceptor).collect(Collectors.toList());
    }

    @Override
    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
        PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);
        PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();
        mpBs.sql(parserSingle(mpBs.sql(), null));
    }

    public String parserSingle(String sql, Object obj) {
        try {
            Statement statement = CCJSqlParserUtil.parse(sql);
            return processParser(statement, 0, sql, obj);
        } catch (JSQLParserException e) {
            return sql;
        }
    }

    private String processParser(Statement statement, int i, String sql, Object obj) {
        for (GenerateHandler handler : getGenerateHandlers().stream().filter(data -> data.off(statement)).collect(Collectors.toList())) {
            if (statement instanceof Insert) {
                insertGenerate(((Insert) statement), handler);
            } else if (statement instanceof Update) {
                updateGenerate(((Update) statement), handler);
            }
        }
        return statement.toString();
    }

    private void updateGenerate(Update statement, GenerateHandler generateHandler) {
        Table table = statement.getTable();
        String fieldName = generateHandler.getFieldName();
        if (MysqlTableFieldCacheUtils.existsFieldInTable(table.getName(), fieldName)) {
            for (UpdateSet updateSet : statement.getUpdateSets()) {
                for (Column column : updateSet.getColumns()) {
                    if (column.getColumnName().equals(fieldName)) {
                        return;
                    }
                }
            }
            statement.addUpdateSet(new Column(fieldName), generateHandler.getValue());
        }
    }

    private void insertGenerate(Insert statement, GenerateHandler generateHandler) {
        // 判断当前表 是否存在 uuid 字段
        if (!MysqlTableFieldCacheUtils.existsFieldInTable(statement.getTable().getName(), generateHandler.getFieldName())) {
            return;
        }
        // 判断 insert into table (id,uuid) 部分的字段 是否已经存在 uuid
        List<Column> list = statement.getColumns();
        for (Column column : list) {
            if (column.getColumnName().equals(generateHandler.getFieldName())) {
                return;
            }
        }
        // 如果没有添加uuid 添加一个uuid 列
        list.add(new Column(generateHandler.getFieldName()));
        statement.setColumns(list);
        insertValuesAppend(statement, generateHandler);

    }

    private void insertValuesAppend(Insert statement, GenerateHandler generateHandler) {
        if (statement.getSelect() == null){
            ItemsList itemsList = statement.getItemsList();
            if (itemsList instanceof  ExpressionList){
                ((ExpressionList) itemsList).addExpressions(generateHandler.getValue());
            }else if (itemsList instanceof MultiExpressionList){
                for (ExpressionList list : ((MultiExpressionList) itemsList).getExpressionLists()) {
                    list.addExpressions(generateHandler.getValue());
                }
            }
            return;
        }
        List<ExpressionList> expressionList = new ArrayList<>();
        if (statement.getSelect().getSelectBody() instanceof PlainSelect) {
            PlainSelect body = (PlainSelect) statement.getSelect().getSelectBody();
            SelectExpressionItem selectExpressionItem = new SelectExpressionItem(generateHandler.getValue());
            selectExpressionItem.setAlias(new Alias("uuid"));
            body.getSelectItems().add(selectExpressionItem);
        } else if (statement.getSelect().getSelectBody() instanceof SetOperationList) {
            expressionList = InterceptorUtils.getSetOperationList(statement);
        }
        for (ExpressionList list : expressionList) {
            list.addExpressions(generateHandler.getValue());
        }
    }
}
